/**
 * 日志工具
 * Logger Utility
 */

const fs = require('fs');
const path = require('path');
const config = require('../config/config');

class Logger {
  constructor() {
    this.logPath = config.logging.path;
    this.level = config.logging.level;
    this.maxSize = this.parseSize(config.logging.maxSize || '10m');
    this.maxFiles = parseInt(config.logging.maxFiles) || 7;
    this.enableConsole = config.logging.enableConsole !== false;
    this.enableFile = config.logging.enableFile !== false;
    
    this.levels = {
      error: 0,
      warn: 1,
      info: 2,
      debug: 3
    };

    // 确保日志目录存在
    this.ensureLogDirectory();
    
    // 启动日志轮转定时器
    this.startLogRotation();
  }

  /**
   * 解析大小字符串
   */
  parseSize(sizeStr) {
    const units = { k: 1024, m: 1024**2, g: 1024**3 };
    const match = sizeStr.toLowerCase().match(/^(\d+(?:\.\d+)?)([kmg]?)$/);
    
    if (match) {
      const value = parseFloat(match[1]);
      const unit = match[2];
      return Math.round(value * (units[unit] || 1));
    }
    
    return 10 * 1024 * 1024; // 默认10MB
  }

  /**
   * 启动日志轮转
   */
  startLogRotation() {
    // 每小时检查一次日志文件大小
    setInterval(() => {
      this.rotateLogsIfNeeded();
    }, 60 * 60 * 1000);

    // 每天清理旧日志文件
    setInterval(() => {
      this.cleanOldLogs();
    }, 24 * 60 * 60 * 1000);
  }

  ensureLogDirectory() {
    if (!fs.existsSync(this.logPath)) {
      fs.mkdirSync(this.logPath, { recursive: true });
    }
  }

  formatMessage(level, message, meta = {}) {
    const timestamp = new Date().toISOString();
    const metaStr = Object.keys(meta).length > 0 ? ` ${JSON.stringify(meta)}` : '';
    return `[${timestamp}] [${level.toUpperCase()}] ${message}${metaStr}\n`;
  }

  writeToFile(level, message, meta) {
    if (!this.enableFile) return;

    const logFile = path.join(this.logPath, `${level}.log`);
    const formattedMessage = this.formatMessage(level, message, meta);

    // 检查文件大小，如果需要则轮转
    this.checkAndRotateLog(logFile);

    fs.appendFile(logFile, formattedMessage, (err) => {
      if (err) {
        console.error('写入日志文件失败 / Failed to write log file:', err);
      }
    });

    // 同时写入到通用日志文件
    const generalLogFile = path.join(this.logPath, 'app.log');
    this.checkAndRotateLog(generalLogFile);
    fs.appendFile(generalLogFile, formattedMessage, () => {});
  }

  /**
   * 检查并轮转日志文件
   */
  checkAndRotateLog(logFile) {
    try {
      if (fs.existsSync(logFile)) {
        const stats = fs.statSync(logFile);
        if (stats.size > this.maxSize) {
          this.rotateLogFile(logFile);
        }
      }
    } catch (error) {
      console.error('检查日志文件大小失败 / Failed to check log file size:', error);
    }
  }

  /**
   * 轮转单个日志文件
   */
  rotateLogFile(logFile) {
    try {
      const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
      const rotatedFile = `${logFile}.${timestamp}`;
      
      fs.renameSync(logFile, rotatedFile);
      
      // 压缩旧日志文件（如果可用）
      this.compressLogFile(rotatedFile);
      
      this.info(`日志文件已轮转 / Log file rotated: ${path.basename(logFile)}`);
    } catch (error) {
      console.error('轮转日志文件失败 / Failed to rotate log file:', error);
    }
  }

  /**
   * 压缩日志文件
   */
  compressLogFile(logFile) {
    try {
      const zlib = require('zlib');
      const readStream = fs.createReadStream(logFile);
      const writeStream = fs.createWriteStream(`${logFile}.gz`);
      const gzip = zlib.createGzip();

      readStream.pipe(gzip).pipe(writeStream);

      writeStream.on('finish', () => {
        // 删除原始文件
        fs.unlinkSync(logFile);
      });
    } catch (error) {
      console.error('压缩日志文件失败 / Failed to compress log file:', error);
    }
  }

  /**
   * 轮转所有日志文件
   */
  rotateLogsIfNeeded() {
    try {
      const files = fs.readdirSync(this.logPath);
      files.forEach(file => {
        if (file.endsWith('.log')) {
          const filePath = path.join(this.logPath, file);
          this.checkAndRotateLog(filePath);
        }
      });
    } catch (error) {
      console.error('轮转日志文件失败 / Failed to rotate logs:', error);
    }
  }

  shouldLog(level) {
    return this.levels[level] <= this.levels[this.level];
  }

  log(level, message, meta = {}) {
    if (!this.shouldLog(level)) {
      return;
    }

    // 控制台输出
    if (this.enableConsole) {
      const timestamp = new Date().toISOString();
      const coloredLevel = this.colorizeLevel(level);
      console.log(`[${timestamp}] [${coloredLevel}] ${message}`);

      if (Object.keys(meta).length > 0) {
        console.log('Meta:', meta);
      }
    }

    // 写入文件
    this.writeToFile(level, message, meta);
  }

  colorizeLevel(level) {
    const colors = {
      error: '\x1b[31m', // 红色
      warn: '\x1b[33m',  // 黄色
      info: '\x1b[36m',  // 青色
      debug: '\x1b[37m'  // 白色
    };
    const reset = '\x1b[0m';
    return `${colors[level] || ''}${level.toUpperCase()}${reset}`;
  }

  error(message, meta = {}) {
    this.log('error', message, meta);
  }

  warn(message, meta = {}) {
    this.log('warn', message, meta);
  }

  info(message, meta = {}) {
    this.log('info', message, meta);
  }

  debug(message, meta = {}) {
    this.log('debug', message, meta);
  }

  /**
   * 清理旧日志文件
   */
  cleanOldLogs() {
    const maxAge = this.maxFiles * 24 * 60 * 60 * 1000; // 根据配置的天数
    const now = Date.now();

    try {
      const files = fs.readdirSync(this.logPath);
      let deletedCount = 0;
      
      files.forEach(file => {
        // 只处理轮转的日志文件（包含时间戳的文件）
        if (file.match(/\.log\.\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}/) || file.endsWith('.gz')) {
          const filePath = path.join(this.logPath, file);
          const stats = fs.statSync(filePath);

          if (now - stats.mtime.getTime() > maxAge) {
            fs.unlinkSync(filePath);
            deletedCount++;
          }
        }
      });

      if (deletedCount > 0) {
        this.info(`清理旧日志文件完成 / Cleaned old log files: ${deletedCount} files deleted`);
      }
    } catch (error) {
      this.error('清理旧日志文件失败 / Failed to clean old log files', { error: error.message });
    }
  }

  /**
   * 获取日志统计信息
   */
  getLogStats() {
    try {
      const files = fs.readdirSync(this.logPath);
      let totalSize = 0;
      let fileCount = 0;

      files.forEach(file => {
        const filePath = path.join(this.logPath, file);
        const stats = fs.statSync(filePath);
        totalSize += stats.size;
        fileCount++;
      });

      return {
        totalFiles: fileCount,
        totalSize: totalSize,
        totalSizeFormatted: this.formatBytes(totalSize),
        logPath: this.logPath
      };
    } catch (error) {
      return {
        error: error.message
      };
    }
  }

  /**
   * 格式化字节数
   */
  formatBytes(bytes) {
    if (bytes === 0) return '0 Bytes';
    const k = 1024;
    const sizes = ['Bytes', 'KB', 'MB', 'GB'];
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
  }
}

module.exports = new Logger();